/**
 * 
 */
package com.csair.youth.web.servlet;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.csair.youth.constant.Constant;
import com.csair.youth.database.Permission;
import com.csair.youth.database.Resource;
import com.csair.youth.database.SysConfig;
import com.csair.youth.service.impl.ResourceServiceImpl;
import com.csair.youth.service.impl.SysConfigServiceImpl;

/**
 * @author mickey
 *
 */
public class ResourceDownloadServlet extends HttpServlet {
	private static final long serialVersionUID = -2026801548944001597L;
	private static final Logger log = Logger.getLogger(ResourceDownloadServlet.class);
	
	private ApplicationContext appContext;
	private ResourceServiceImpl resourceService;
	private SysConfigServiceImpl sysConfigService;
	
	//downloadMap,用于监控下载线程,只允许单线程下载!
	private Map<String, List<String>> downloadMap = new HashMap<String, List<String>>();
	
	@Override
	public void init() throws ServletException {
		// TODO Auto-generated method stub
		log.warn("ResourceDownloadServlet init...");
		super.init();
		appContext = WebApplicationContextUtils.getRequiredWebApplicationContext(this.getServletContext());
		resourceService = (ResourceServiceImpl)appContext.getBean("resourceService");
		sysConfigService = (SysConfigServiceImpl)appContext.getBean("sysConfigService");
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		doPost(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		//由于是自定义servlet，所以应该先判断资源库是否已关闭，struts层面上的通过interceptor实现
		SysConfig config = sysConfigService.getSysConfigByKey(Constant.REPO_STATUS_KEY);
		SysConfig speedConfig = sysConfigService.getSysConfigByKey(Constant.DOWNLOAD_SPEED_KEY);
		int maxSpeed = Integer.parseInt(speedConfig.getConfigValue());
		if(config == null || StringUtils.equals(config.getConfigValue(), Constant.REPO_STATUS_CLOSE)){
			//关闭了...
			log.error("资源库已关闭");
			resp.sendRedirect("RepoClosed.action");
			return;
		}
		String rid = req.getParameter("p");
		Resource res = resourceService.getResource(rid);
		if(res == null || !(new File(res.getAbsolutePath())).exists()){
			//找不到记录
			log.error("找不到资源记录或文件不存在:"+rid);
			resp.sendRedirect("ResourceNotFound.action");
			return;
		}
		String auditResult = res.getAuditResult();
		if(!Constant.RESOURCE_AUDIT_PASS.equals(auditResult)){
			//未有审核结果或者审核没通过的
			//若是信息审核员，则可以提供下载
			//若是普通用户，则转到错误页面
			List permissions = (List)req.getSession().getAttribute(Constant.USER_PERMISSION_MAP_KEY);
			if(permissions == null || permissions.size() == 0){
				log.error("当前登录用户没有执行资源审核权限[1],用户:"+(String)req.getSession().getAttribute(Constant.USER_NAME_SESSION_KEY)+" 资源:"+rid);
				resp.sendRedirect("ResourceNoAudit.action");
				return;
			}
			else{
				boolean isAuditor = false;
				Iterator iter = permissions.iterator();
				while(iter.hasNext()){
					Permission p = (Permission)iter.next();
					if(p.getUrl().equals("/ResourceAudit.action")) isAuditor = true;
				}
				if(isAuditor){
					doDownload(req, resp, res, maxSpeed);
				}
				else{
					log.error("当前登录用户没有执行资源审核权限[2],用户:"+(String)req.getSession().getAttribute(Constant.USER_NAME_SESSION_KEY)+" 资源:"+rid);
					resp.sendRedirect("/ResourceNoAudit.action");
					return;
				}
			}
		}
		else{
			//已经审核通过的资源
			doDownload(req, resp, res, maxSpeed);
		}
	}
	
	private void doDownload(HttpServletRequest req, HttpServletResponse resp, Resource item, int maxSpeed){
		log.warn("文件下载请求from ip:"+req.getRemoteAddr()+"  Resource:"+item.getId()+"\n");
		if(isCurrentIpDownloadingThisResource(req.getRemoteAddr(), item.getId())){
			//已经正在download，不能在当前下载过程再次请求下载
			log.error("文件下载请求被拦截，该IP已经有线程在下载此资源!ip:"+req.getRemoteAddr()+" Resource:"+item.getId());
			return;
		}
		else{
			//+1
			log.warn("文件下载请求允许，ip:"+req.getRemoteAddr()+" Resource:"+item.getId());
			putThisResourceToCurrentIp(req.getRemoteAddr(), item.getId());
		}
		
		int sleepTime = 0;
		if(maxSpeed > 0){
			int timeCount = maxSpeed/4;  	//除以4因为缓冲区是4K大小
			sleepTime = 1000/timeCount;  //ms
		}
		resp.reset();
		long pos = 0;
		OutputStream os = null;
		FileInputStream is = null;
		try{
			File f = new File(item.getAbsolutePath());
			is = new FileInputStream(f);
			long fSize = f.length();
			byte xx[] = new byte[4096];
			resp.setHeader("Accept-Ranges", "bytes");
			resp.setHeader("Content-Length", fSize+"");
			resp.setHeader("Content-Disposition", "attachment;filename="+new String(item.getFileName().getBytes("gbk"), "iso_8859_1"));
			resp.setHeader("Content-Type", item.getContentType()+"; charset=utf-8");
			if(req.getHeader("Range")!=null){
				//若客户端传来Range，说明之前下载了一部分，设置206状态(SC_PARTIAL_CONTENT)
				resp.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
				pos = Long.parseLong(req.getHeader("Range").replaceAll("bytes=", "").replaceAll("-", ""));
			}
			if(pos != 0){
				String contentRange = new StringBuffer("bytes ").append(new Long(pos).toString()).append("-").append(new Long(fSize - 1).toString()).append("/").append(new Long(fSize).toString()).toString();
				resp.setHeader("Content-Range", contentRange);
//				resp.setHeader("Connection", "keepalive");
//				System.out.println("Content-Range="+contentRange);
				//略过已经传输过的字节
				is.skip(pos);
			}
			os = resp.getOutputStream();
			boolean all = false;
			while(!all){
				int n = is.read(xx);
				if(n != -1){
					os.write(xx, 0, n);
					if(sleepTime > 0){
						try {
							Thread.sleep(sleepTime);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
				else{
					all = true;
				}
			}
			item.setDownloadTimes(item.getDownloadTimes()+1);
			item.setLastModifyTime(new Date());
			resourceService.saveResource(item);
		}catch(IOException e){
			e.printStackTrace();
			log.error("文件下载过程中发生IO异常!");
			return;
		}finally{
			//download map clear...
			log.warn("下载完成或IO错误，removeThisResourceOfCurrentIp! ip:"+req.getRemoteAddr()+" Resource:"+item.getId());
			removeThisResourceOfCurrentIp(req.getRemoteAddr(), item.getId());
			
			if(is != null){
				try {
					is.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if(os != null){
				try {
					os.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
	
	private synchronized boolean isCurrentIpDownloadingThisResource(String ip, String resourceId){
		List<String> list = downloadMap.get(ip);
		if(list != null && list.size() > 0){
			Iterator<String> iter = list.iterator();
			while(iter.hasNext()){
				String rid = iter.next();
				if(rid.equals(resourceId)) return true;
			}
			return false;
		}
		else return false;
	}
	
	private synchronized void putThisResourceToCurrentIp(String ip, String resourceId){
		if(StringUtils.isEmpty(ip)){
			return;
		}
		List<String> list = downloadMap.get(ip);
		if(list == null) {
			list = new ArrayList<String>();
			list.add(resourceId);
			downloadMap.put(ip, list);
		}
		else{
			list.add(resourceId);
		}
	}
	
	private synchronized void removeThisResourceOfCurrentIp(String ip, String resourceId){
		if(StringUtils.isEmpty(ip)){
			return;
		}
		List<String> list = downloadMap.get(ip);
		if(list != null && list.size() > 0){
			Iterator<String> iter = list.iterator();
			while(iter.hasNext()){
				String rid = iter.next();
				if(rid.equals(resourceId)){
					iter.remove();
				}
			}
		}
	}
}
